-
Notifications
You must be signed in to change notification settings - Fork 106
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Reject future blocks #492
Reject future blocks #492
Conversation
Codecov Report
@@ Coverage Diff @@
## main #492 +/- ##
==========================================
+ Coverage 53.05% 53.42% +0.37%
==========================================
Files 68 80 +12
Lines 3212 3942 +730
==========================================
+ Hits 1704 2106 +402
- Misses 1508 1836 +328
Continue to review full report at Codecov.
|
In this case, the time check is a quick check, so we can just do it in But we'll still need PR #491 to do expensive checks in the async block. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Writing a test to cover this seems annoying to set up so I'm approving without a test, but we may want to create an issue to make one and make it open to contributors / good-first-issue.
zebra-consensus/src/verify.rs
Outdated
/// Returns true if the block header time is less than or equal to | ||
/// 2 hours in the future, according to the node's local clock. | ||
/// | ||
/// This is a non-deterministic rule, as clocks vary over time, and |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
☝️
zebra-consensus/src/verify.rs
Outdated
/// This is a non-deterministic rule, as clocks vary over time, and | ||
/// between different nodes. | ||
/// | ||
/// "In addition, a full validator MUST NOT accept blocks with nTime |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍👍👍
I think a test for "2 hours in the future" should be reasonably easy, let me see if I can set one up. It will be harder to test overflow, I might need to refactor to do that. |
9957d0c
to
aa98871
Compare
I rebased this branch to handle the conflicts with the comments I cherry-picked from PR #491.
I tested:
The |
I've set this PR to draft, because I need to fix an issue with early state commits, as discussed here: I should also add some tests, to make sure that the state isn't changed on verification errors. |
zebra-consensus/src/verify.rs
Outdated
// | ||
// `tower::Buffer` expects exactly one `call` for each | ||
// `poll_ready`. So we unconditionally create the AddBlock | ||
// Future using `state_service.call`. If verification fails, | ||
// we return an error, and implicitly cancel the future. | ||
let add_block = self.state_service.call(zebra_state::Request::AddBlock { | ||
block: block.clone(), | ||
}); | ||
|
||
async move { | ||
// If verification fails, return an error result. | ||
// The AddBlock Future is implicitly cancelled by the | ||
// error return in `?`. | ||
|
||
// Since errors cause an early exit, try to do the | ||
// quick checks first. | ||
block::node_time_check(block)?; | ||
|
||
// Verification was successful. | ||
// Add the block to the state by awaiting the AddBlock | ||
// Future, and return its result. | ||
match add_block.await? { | ||
zebra_state::Response::Added { hash } => Ok(hash), | ||
_ => Err("adding block to zebra-state failed".into()), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed in #502, we can't conditionally await an AddBlock future. Because the state service can commit changes in call()
, or in the Future's async block (that is, in await).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 589f1ce.
zebra-consensus/src/verify/block.rs
Outdated
/// | ||
/// [7.5]: https://zips.z.cash/protocol/protocol.pdf#blockheader | ||
pub(super) fn node_time_check(block: Arc<Block>) -> Result<(), Error> { | ||
node_time_check_helper(block.header.time, Utc::now()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TIOLI: I don't think this benefits from being broken out into a separate function, I'd prefer to start the fn with
let block_header_time = block.header.time;
let now = Utc::now();
// The rest of node_time_check_helper's function body...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right, it made more sense before I split out the node_time_check_helper() for testing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 347f5ba.
I think we can let GitHub squash and merge this PR. (I could do a manual squash into a time check commit, a BlockVerifier refactor commit, and other changes. Happy to do that if people think it's worth the effort.) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All these tests fill me with joy
// TODO(teor || jlusby): check error string | ||
_ => true, | ||
}, | ||
// TODO(teor || jlusby): check error string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wouldn't default to doing string comparisons, if there's ever a specific error that we need to check for that is being constructed from str -> Box<dyn Error>
we should instead change the error to be a proper error type that we can downcast to, using string's content as a proxy for a typed error is a recipe for disaster.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll fix these comments, possibly in a future PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TIOLI: I think the BlockVerifier
belongs in the block
submodule, and generally I think we should keep all of the contents of the code in the leaves of the module tree and then re-export as necessary.
Looks like this needs a rebase but LGTM! 💃 |
We don't need to check transaction sizes yet, because we aren't parsing or generating transactions outside of blocks. Part of ZcashFoundation#483.
Add a function that checks the block header miner times against the node's local clock. (We'll use this function in the next commit.) Part of ZcashFoundation#477.
Use MAX constants for the block header version and time arbitrary test ranges. Reduces the block header time arbitrary test range from 2**32 to 2**32-1 (u32::MAX). (2**32 is an invalid time value, which gets truncated to 0 during serialization.) Also add some comments about DateTime conversions. Part of ZcashFoundation#477.
We don't want to call the state's AddBlock until we know the block is valid. This avoids subtle bugs if the state is modified in call(). (in_memory currently modifies the state in call(), on_disk modifies the state in the async block.) Part of ZcashFoundation#477.
node_time_check() is a small function, so we inline it into its callers. (And then rename node_time_check_helper() to node_time_check().) Part of ZcashFoundation#477.
153a968
to
20dacb9
Compare
I rebased and made the following changes:
I'll merge after CI passes, I don't think the differences are large enough for a review. |
The checks passed, and the changes are minor, so I merged this PR. |
Reject blocks that are more than 2 hours in the future, according to the node's clock.
I'll be able to complete this PR, once we've finished the asyncAddBlock()
changes in PR #491.Edit: PR #491 wasn't actually needed.